#include "vim.h"
#include "misc2.pro"

typedef unsigned short ush;	/* unsigned 16-bit value */
typedef unsigned long  ulg;	/* unsigned 32-bit value */

static void make_crc_tab __ARGS((void));
static ulg crc_32_tab[256];

#define CRC32(c, b) (crc_32_tab[((int)(c) ^ (b)) & 0xff] ^ ((c) >> 8))
static ulg keys[3]; /* keys defining the pseudo-random sequence */

#define DECRYPT_BYTE_ZIP(t) { \
    ush temp; \
 \
    temp = (ush)keys[2] | 2; \
    t = (int)(((unsigned)(temp * (temp ^ 1U)) >> 8) & 0xff); \
}

#define UPDATE_KEYS_ZIP(c) { \
    keys[0] = CRC32(keys[0], (c)); \
    keys[1] += keys[0] & 0xff; \
    keys[1] = keys[1] * 134775813L + 1; \
    keys[2] = CRC32(keys[2], (int)(keys[1] >> 24)); \
}

static int crypt_busy = 0;
static ulg saved_keys[3];
static int saved_crypt_method;

static void make_crc_tab() 
{
    ulg		s,t,v;
    static int	done = FALSE;
    
    if (done)
        return;
    
    for (t = 0; t < 256; t++) {
        v = t;
        for (s = 0; s < 8; s++)
            v = (v >> 1) ^ ((v & 1) * (ulg)0xedb88320L);
        crc_32_tab[t] = v;
    }
    done = TRUE;
}

void crypt_init_keys(char_u *passwd)
{
    if (passwd != NULL && *passwd != NUL) {
        char_u *p;
        
        make_crc_tab();
        keys[0] = 305419896L;
        keys[1] = 591751049L;
        keys[2] = 878082192L;
        
        for (p = passwd; *p!= NUL; ++p) {
            UPDATE_KEYS_ZIP((int)*p);
        }
    }
}

void crypt_encode(char_u *from, size_t len, char_u *to)
{
    size_t	i;
    int		ztemp, t;
    
    for (i = 0; i < len; ++i) {
        ztemp = from[i];
        DECRYPT_BYTE_ZIP(t);
        UPDATE_KEYS_ZIP(ztemp);
        to[i] = t ^ ztemp;
    }
}

void crypt_decode(char_u *ptr, long len)
{
    for (char_u *p = ptr; p < ptr + len; ++p) {
        ush temp;
        
        temp = (ush)keys[2] | 2;
        temp = (int)(((unsigned)(temp * (temp ^ 1U)) >> 8) & 0xff);
        UPDATE_KEYS_ZIP(*p ^= temp);
    }
}

void crypt_push_state() 
{
    if (crypt_busy == 1) {
        saved_keys[0] = keys[0];
        saved_keys[1] = keys[1];
        saved_keys[2] = keys[2];
        saved_crypt_method = 0;
    } else if (crypt_busy > 1) {
        printf("*******crypt_push_state_error\n");
    }
    ++crypt_busy;
}

void crypt_pop_state()
{
    --crypt_busy;
    
    if (crypt_busy == 1) {
        keys[0] = saved_keys[0];
        keys[1] = saved_keys[1];
        keys[2] = saved_keys[2];
    }
}